/*
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.dspl.validate.validator;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.dspl.model.Attribute;
import com.google.dspl.model.Concept;
import com.google.dspl.model.DataType;
import com.google.dspl.model.Identifier;
import com.google.dspl.validate.DatasetResolver;
import com.google.dspl.validate.ItemReference;
import com.google.dspl.validate.ValidationContext;
import com.google.dspl.validate.violation.ConceptAttributeConceptNotFound;
import com.google.dspl.validate.violation.ConceptAttributeConflictingParentTypeViolation;
import com.google.dspl.validate.violation.ConceptAttributeConflictingValueConceptTypeViolation;
import com.google.dspl.validate.violation.ConceptAttributeNoIdViolation;
import com.google.dspl.validate.violation.ConceptAttributeNoTypeViolation;
import com.google.dspl.validate.violation.ConceptAttributeValueConceptDoesNotExtendParentConcept;
import com.google.dspl.validate.violation.Violation;

import java.util.List;

/**
 * {@link Validator} for {@link Attribute}s on {@link Concept}s.
 *
 * @author Shardul Deo
 */
class ConceptAttributeValidator implements Validator<Attribute> {

  private final Concept enclosingConcept;

  ConceptAttributeValidator(Concept concept) {
    this.enclosingConcept = Preconditions.checkNotNull(concept);
  }

  @Override
  public List<Violation> validate(Attribute attribute, ValidationContext context) {
    String datasetId = context.getCurrentDataset().getDatasetId();
    ItemReference attributeReference =
        ItemReference.reference(context.getCurrentDataset(), enclosingConcept, attribute);
    ImmutableList.Builder<Violation> violations = ImmutableList.builder();

    String attributeId = null;
    Identifier valueConcept = attribute.getConcept();
    if (Strings.isNullOrEmpty(attribute.getId()) && valueConcept == null) {
      violations.add(
          new ConceptAttributeNoIdViolation(attributeReference, enclosingConcept.getId()));
    } else {
      if (attribute.getId() != null) {
        IdValidator validator = new IdValidator(attributeReference);
        validator.validate(attribute.getId(), violations);
      }

      attributeId =
          attribute.getId() != null ? attribute.getId() : valueConcept.getObjectId();
    }

    DatasetResolver datasetResolver = context.getDatasetResolver();
    DataType dataType = attribute.getType();
    Concept parentConcept = null;
    if (enclosingConcept.getParent() != null) {
      parentConcept = datasetResolver.getConcept(enclosingConcept.getParent());
    }
    if (dataType != null && parentConcept != null && attributeId != null) {
      // Check type matches type inherited from parent
      DataType parentType =
          datasetResolver.resolveConceptAttributeType(datasetId, attributeId, parentConcept);
      if (parentType != null && dataType != parentType) {
        violations.add(new ConceptAttributeConflictingParentTypeViolation(
            attributeReference,
            attributeId,
            enclosingConcept.getId(),
            dataType,
            parentType));
      }
    }

    if (valueConcept != null) {
      if (!ValidationHelper.isValidConceptReference(valueConcept, context)) {
        violations.add(new ConceptAttributeConceptNotFound(
            attributeReference,
            attributeId,
            enclosingConcept.getId(),
            valueConcept));
      } else {
        Concept concept = datasetResolver.getConcept(valueConcept);

        // Check type matches
        DataType conceptType = datasetResolver.resolveConceptType(datasetId, concept);
        if (dataType == null) {
          dataType = conceptType;
        } else if (conceptType != null && dataType != conceptType) {
          violations.add(new ConceptAttributeConflictingValueConceptTypeViolation(
              attributeReference,
              attributeId,
              enclosingConcept.getId(),
              dataType,
              valueConcept,
              conceptType));
        }

        if (attributeId != null && parentConcept != null) {
          // Check concept is same or sub-concept of inherited concept reference
          Identifier inheritedConcept = datasetResolver.resolveConceptAttributeReferencedConcept(
              datasetId, attributeId, parentConcept);
          if (inheritedConcept != null) {
            if (!valueConcept.equals(inheritedConcept)
                && !datasetResolver.isParent(datasetId, concept, inheritedConcept)) {
              violations.add(new ConceptAttributeValueConceptDoesNotExtendParentConcept(
                  attributeReference,
                  attributeId,
                  enclosingConcept.getId(),
                  inheritedConcept,
                  valueConcept));
            }
          }
        }
      }
    }
    if (dataType == null && attributeId != null) {
      dataType =
          datasetResolver.resolveConceptAttributeType(datasetId, attributeId, enclosingConcept);
    }
    if (dataType == null) {
      violations.add(new ConceptAttributeNoTypeViolation(
          attributeReference, attributeId, enclosingConcept.getId()));
    }

    return violations.build();
  }
}
